using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

public class ChangeMethod : MonoBehaviour
{

    internal static Dictionary<int, List<CheckActicns>> valuePairs = new Dictionary<int, List<CheckActicns>>();
    static ChangeMethod _instance;
    internal static ChangeMethod instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new GameObject("ChangeController").AddComponent<ChangeMethod>();
                DontDestroyOnLoad(_instance.gameObject);
            }
            return _instance;
        }
    }

    public void Update()
    {
        foreach (var actions in valuePairs.Values)
        {
            foreach (var actionStruct in actions)
            {
                actionStruct.action?.Invoke();
            }
        } 
    }
}


public class ChangeAttribute : System.Attribute
{
    public string methodStr;
    public bool isInitCall;

    public ChangeAttribute(bool isInitCall = false)
    {
       this.isInitCall = isInitCall;
    }

    public ChangeAttribute(string methodStr, bool isInitCall = false)
    {
        this.methodStr  = methodStr;
        this.isInitCall = isInitCall;
    }
}


public struct CheckActicns
{
    public System.Action release;
    public System.Action action;
}


public class CheckData
{
    public FieldInfo field;
    public MethodInfo method;

    public object lastValue;
    public object instance;


    public void Init()
    {
        ValueCall(field.GetValue(instance));
    }
     

    public void Check()
    {     
        // 执行代码
        var value = field.GetValue(instance);

        if (value != lastValue)
        {
            ValueCall  (value);
            lastValue = value;
        }
    }

    public void ValueCall(object value)
    {
        var count = method.GetParameters().Length;
        if (count == 0)
        {
            method?.Invoke(instance, null);
        }
        else if (count == 1)
        {
            method?.Invoke(instance, new object[] { value });
        }
        else if (count == 2)
        {
            method?.Invoke(instance, new object[] { value, lastValue });
        }
    }

    public void Release()
    {
        field     = null;
        method    = null;
        instance  = null;
        lastValue = null;
    }
}


public static class ChangeEventExtend
{
    public static void InitChangeEvent<T>(this T instance)
    {

        if (ChangeMethod.instance == false)
        { 
            Debug.Log("ChangeEvent.instance is null");
        }

        var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
        var actions = new List<CheckActicns>();

        foreach (var field in fields)
        {
            var checkEvent = field.GetCustomAttribute<ChangeAttribute>();

            if (checkEvent != null)
            {
                if (field.FieldType.IsArray || field.FieldType.IsGenericType)
                {
                    Debug.LogError($"{instance} -> [{field.Name}] ChangeAttribute can't support Array or GenericType");
                    continue;
                }

                var method     = typeof(T).GetMethod(checkEvent.methodStr, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
                var check      = new CheckData()    { method = method, field = field, instance = instance };
                var action     = new CheckActicns() { action = check.Check, release = check.Release };

                if(checkEvent.isInitCall)
                {
                    check.Init();
                }
                
                actions.Add(action);
            }
        }
        ChangeMethod.valuePairs[instance.GetHashCode()] = actions;
    }

    public static void ReleaseChangeEvent<T>(this T instance)
    {
        if (ChangeMethod.valuePairs.TryGetValue(instance.GetHashCode(), out List<CheckActicns> actions))
        {
            foreach (var action in actions)
            {
                action.release?.Invoke();
            }
            actions.Clear();
            ChangeMethod.valuePairs.Remove(instance.GetHashCode());
        }
    }


}

